#ifndef __tenacitas_translator_core__file_translator__h__
#define __tenacitas_translator_core__file_translator__h__

/** @file */

/** @todo */

// ==> C++ Headers
#include <map>
#include <string>
#include <fstream>
#include <sstream>

// ==> 3rds Headers

// ==> Our Headers
#include <tenacitas.return_code.core/rc.h>
#include <tenacitas.log.core/logger.h>

// ==> Namespaces
using namespace std;

// ==> Macro Constants

// ==> Macro Commands


/** ==> organization */
namespace tenacitas { 
    /** ==> project */
    namespace translate {
        /** ==> perspective */
        namespace core {

            // ==> Typedefs 

            // ==> Pre-Declarations

            // ==> Attributes 

            // ==> Functions

            // ==> Classes

            /** 
                @tparam supplier must provide an implementation of
                tenacitas::log::core::logger 
                @nosubgrouping 
            */
            template <typename supplier>
            class file_translator {

                // ==> file_translator - Friends

                /** Class allowed to access private members of @p
                    file_translator, in order to perform tests */
                friend class file_translator_tester;

            public:

                // ==> file_translator - Public Internal Types

                /** */
                typedef typename supplier::logger logger;

                /** */
                typedef class_id size_type;

                enum constants : constant_id {
                    class_id_sep_not_found = 1, 
                        file_not_found = 2, 
                        cte_id_sep_not_found = 3, 
                        error_adding_translation = 4,
                        translation_not_found = 5
                        };
                
                // ==> file_translator - PUBLIC Constructors

                /** */
                inline file_translator(const std::string & p_file_name) noexcept;

                /** */
                inline file_translator() noexcept;

                /** */
                file_translator(const file_translator &) = delete;

                /** */
                file_translator(file_translator &&) = delete;
                
                /** Destructor */
                inline ~file_translator() noexcept;

                // ==> file_translator - Public Accessors


                /** */
                inline size_type size() noexcept;
                
                /** */
                rc<> add(const std::string & p_file_name) noexcept;

                // ==> file_translator - Public Helpers

                /** */
                template <typename t_out>
                inline void print(t_out & p_out) const noexcept;
                
                // ==> file_translator - Public Processors

                // ==> file_translator - Public Operators

                /** */
                inline std::string 
                operator()(const class_id & p_class,
                           const constant_id & p_constant) noexcept;  

                /** */
                template <typename obj>
                inline std::string operator()(const rc<obj> &) noexcept;

                /** */
                file_translator & operator=(const file_translator &)=delete;

                /** */
                file_translator & operator=(file_translator &&)=delete;

                /** not allowed */
                void* operator new (std::size_t size)
                    throw (std::bad_alloc) = delete; 

                /** not allowed */
                void operator delete (void *p_class_id) = delete;

                /** not allowed */
                void *operator new[] (size_t) = delete;

                /** not allowed */
                void operator delete[] (void *) = delete;

                /** not allowed */
                bool operator ==(const file_translator & )=delete;

                /** not allowed */
                bool operator !=(const file_translator & )=delete;

                /** not allowed */
                bool operator >=(const file_translator & )=delete;

                /** not allowed */
                bool operator >(const file_translator & )=delete;

                /** not allowed */
                bool operator <=(const file_translator & )=delete;

                /** not allowed */
                bool operator <(const file_translator & )=delete;
                

                // ==> file_translator - Public Attributes

                static const class_id id = 14286139863807ULL;

                                                
            private:

                // ==> file_translator - Private Internal Types

                /** */
                struct text_id {
                    class_id m_class;
                    constant_id m_constant;

                    inline text_id(class_id && p_class,
                                   constant_id && p_constant) {
                        m_class = std::move(p_class);
                        m_constant = std::move(p_constant);
                    }

                    inline text_id(const class_id & p_class,
                                   const constant_id & p_constant)
                        : m_class (p_class),
                          m_constant (p_constant){}

                    inline text_id(text_id && p_text_id) {
                        m_class = std::move(p_text_id.m_class);
                        m_constant = std::move(p_text_id.m_constant);
                    }

                    text_id(const text_id & p_text_id) = delete;
                    
                    inline bool operator ==(const text_id & p_text_id) const {
                        return ( (m_class == p_text_id.m_class) &&
                                 (m_constant == p_text_id.m_constant));
                    }
                    
                    inline bool operator !=(const text_id & p_text_id) const {
                        return  ( (m_class != p_text_id.m_class) ||
                                  (m_constant != p_text_id.m_constant));
                    }

                    bool operator >=(const text_id & p_text_id) const {
                        if (m_class > p_text_id.m_class) {
                            return true;
                        }
                        
                        if (m_class < p_text_id.m_class) {
                            return false;
                        }

                        if (m_constant > p_text_id.m_constant) {
                            return true;
                        }

                        if (m_constant < p_text_id.m_constant) {
                            return false;
                        }

                        return true;
                    }
                    
                        
                    bool operator >(const text_id & p_text_id) const {
                        if (m_class > p_text_id.m_class) {
                            return true;
                        }
                        
                        if (m_class < p_text_id.m_class) {
                            return false;
                        }

                        if (m_constant > p_text_id.m_constant) {
                            return true;
                        }

                        if (m_constant < p_text_id.m_constant) {
                            return false;
                        }

                        return false;
                    }
                    
                    bool operator <=(const text_id & p_text_id) const {
                        if (m_class < p_text_id.m_class) {
                            return true;
                        }
                        
                        if (m_class > p_text_id.m_class) {
                            return false;
                        }

                        if (m_constant < p_text_id.m_constant) {
                            return true;
                        }

                        if (m_constant > p_text_id.m_constant) {
                            return false;
                        }

                        return true;
                    }
                    
                    bool operator <(const text_id & p_text_id) const {
                        if (m_class < p_text_id.m_class) {
                            return true;
                        }
                        
                        if (m_class > p_text_id.m_class) {
                            return false;
                        }

                        if (m_constant < p_text_id.m_constant) {
                            return true;
                        }

                        if (m_constant > p_text_id.m_constant) {
                            return false;
                        }

                        return false;
                    }

                    text_id & operator=(text_id && p_text_id) {
                        if (this != & p_text_id) {
                            m_class = std::move(p_text_id.m_class);
                            m_constant = std::move(p_text_id.m_constant);
                        }
                        return *this;
                    }                    

                    text_id & operator=(const text_id & p_text_id) = delete;
                    
                };

                /** */
                typedef std::map<text_id, std::string> translations;


                rc<std::string>
                find(const class_id & p_class,
                     const constant_id & p_constant) noexcept;
                
                /** */
                bool add(class_id && p_class,
                         constant_id && p_cte,
                         std::string && p_trans) noexcept;

                logger m_logger;
                
                /** */
                static translations m_translations;

            };
        }
    }
}
// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::translate::core;


// -----------------------------------------------------------------------------
template <typename supplier>
const class_id 
file_translator<supplier>::id;

// -----------------------------------------------------------------------------
template <typename supplier>
typename file_translator<supplier>::translations 
file_translator<supplier>::m_translations;

// -----------------------------------------------------------------------------
template <typename supplier>
file_translator<supplier>::
file_translator() noexcept {}

// -----------------------------------------------------------------------------
template <typename supplier>
file_translator<supplier>::
file_translator(const std::string & p_file_name) noexcept {
    add(p_file_name);
}

// -----------------------------------------------------------------------------
template <typename supplier>
file_translator<supplier>::
~file_translator() noexcept {}


// -----------------------------------------------------------------------------
template <typename supplier>
std::string
file_translator<supplier>::
operator()(const class_id & p_class,
           const constant_id & p_constant) noexcept {
    rc<std::string> _rc = find(p_class, p_constant);
    if (!_rc) {
        std::stringstream _stream;
        _stream << p_class << ", " << p_constant;
        return _stream.str();
    }
    return _rc.get_obj();
}

// -----------------------------------------------------------------------------
template <typename supplier>
typename file_translator<supplier>::size_type
file_translator<supplier>::
size() noexcept {
    return m_translations.size();
}

// -----------------------------------------------------------------------------
template <typename supplier>
template <typename t_out>
void
file_translator<supplier>::
print(t_out & p_out) const noexcept {
    typename translations::const_iterator _end = m_translations.end();
                        
    for (typename translations::const_iterator _ite =
             m_translations.begin();
         _ite != _end;
         ++_ite) {
        p_out << "class = " << _ite->first.m_class
              << ", cte = " << _ite->first.m_constant
              << ", trans = '" << _ite->second << "'" <<
            std::endl;
    }

}

// -----------------------------------------------------------------------------
template <typename supplier>
template <typename obj>
inline std::string
file_translator<supplier>::
operator()(const rc<obj> & p_rc) noexcept {
    rc<std::string> _rc (find(p_rc.get_class_id(),
                              p_rc.get_constant_id())); 
    std::stringstream _stream;
    if (!_rc) {
        _stream << p_rc;
    }
    else {
        _stream << _rc.get_obj() << ", "
                << p_rc.get_file()  << ", "
                << p_rc.get_line();
        if (!p_rc.get_desc().empty()) {
            _stream << ", " << p_rc.get_desc();
        }
    }
    return _stream.str();
}

// -----------------------------------------------------------------------------
template <typename supplier>
rc<std::string>
file_translator<supplier>::
find(const class_id & p_class,
     const constant_id & p_constant)  noexcept {

    auto element =
        m_translations.find(text_id(p_class,p_constant)); 

    if (element != m_translations.end()) {
        std::string _trans = element->second;
        return rc<std::string>(std::move(_trans));
    }
    rc<std::string> _rc(id,
                        constants::translation_not_found,
                        __FILE__, __LINE__);
    log_error(m_logger) << _rc;
    
    return _rc;    
}

// -----------------------------------------------------------------------------
template <typename supplier>
bool
file_translator<supplier>::
 add(class_id && p_class,
         constant_id && p_cte,
         std::string && p_trans) noexcept {
    try {
        m_translations.
            emplace(std::move(text_id(p_class,p_cte)),
                    std::move(p_trans));
    } catch (std::exception & _e) {
        // @todo log exception
        return false;                            
    }
    return true;
}


// -----------------------------------------------------------------------------
template <typename supplier>
rc<>
file_translator<supplier>::
add(const std::string & p_file_name) noexcept {

    std::ifstream _file(p_file_name);

    if (!_file.good()) {
        rc<> _rc(id, constants::file_not_found,__FILE__, __LINE__);
        log_error(m_logger) << _rc;
        return _rc;
    }

    std::string _line;

    std::string _aux;

    std::string::size_type _1 = 0;
    std::string::size_type _2 = 0;
    class_id _class;
    constant_id _cte;
    std::string _trans;
    
    while (true) {

        _line.clear();
        
        std::getline(_file, _line, '\n');

        _1 = _2 = 0;

        if ( (_line.empty()) && (_file.eof()) ) {
            break;
        }

        _2 = _line.find('|', _1 );
        if (_2 == std::string::npos ) {
            rc<> _rc(id, constants::class_id_sep_not_found,
                     __FILE__,__LINE__); 
            log_error(m_logger) << _rc;
            return _rc;            
        }

        _aux = std::move(std::string(&_line[_1], &_line[_2]));
        _1 = _2 + 1;

        _class = std::move(std::stoull(_aux));

        _2 = _line.find('|', _1 );
        if (_2 == std::string::npos ) {
            rc<> _rc(id, constants::cte_id_sep_not_found,
                     __FILE__, __LINE__); 
            log_error(m_logger) << _rc;
            return _rc;            
        }

        _aux = std::move(std::string(&_line[_1], &_line[_2]));
        _1 = _2 + 1;
        
        _cte  = std::move(std::stoull(_aux));

        _trans = std::move(_line.substr(_1, _line.size() - _1 ));

        rc<std::string> _rc(find(_class, _cte));

        
        if (!_rc) {
            if (!add(std::move(_class),
                     std::move(_cte),
                     std::move(_trans))) {
                rc<> _rc(id, constants::error_adding_translation,
                         __FILE__, __LINE__);
                log_error(m_logger) << _rc;                 
                return _rc;                 
            }
        }
    }
    return rc<>();
}



#endif
